home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
QRZ! Ham Radio 8
/
QRZ Ham Radio Callsign Database - Volume 8.iso
/
pc
/
files
/
t_jnos
/
j109lxa4.tgz
/
j109lxa4.tar
/
unixasy.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-06-04
|
16KB
|
780 lines
/*
* File stolen from WAMPES 921229, modified for compatibility with JNOS and my
* tastes, and left to sink or swim. Blub! ++bsa
*
* The actual structure is much closer to that of JNOS than to WAMPES. The
* reason is that WAMPES uses these weirdball I/O hooks... We will use the
* "classic" interface, modified by the use of register_fd(). (Actually, the
* weirdball I/O hooks are just WAMPES's version of register_fd(). The API
* for WAMPES is a heck of a lot hairier, though.)
*/
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <sys/time.h>
#include <errno.h>
#include "global.h"
#include "mbuf.h"
#include "proc.h"
#include "iface.h"
#include "asy.h"
#include "timer.h"
#include "unixasy.h"
#include "hardware.h"
#include "devparam.h"
#include "commands.h"
#include "cmdparse.h"
extern int chmod __ARGS((const char *, unsigned));
static int find_speed __ARGS((long speed));
static void pasy __ARGS((struct asy *asyp));
static void asy_tx __ARGS((int, void *, void *));
static void asy_input __ARGS((int, void *, void *));
struct asy Asy[ASY_MAX];
/*---------------------------------------------------------------------------*/
static int dorxqueue __ARGS((int argc, char **argv, void *p));
static int dotxqueue __ARGS((int argc, char **argv, void *p));
static int dobufsize __ARGS((int argc, char **argv, void *p));
static int doasy2stat __ARGS((int argc, char **argv, void *p));
static struct cmds AsyPcmds[] =
{
"rxqueue", dorxqueue, 0, 0, NULLCHAR,
"txqueue", dotxqueue, 0, 0, NULLCHAR,
"bufsize", dobufsize, 0, 0, NULLCHAR,
"status", doasy2stat, 0, 0, NULLCHAR,
0,
};
/*---------------------------------------------------------------------------*/
static struct {
long speed;
speed_t flags;
} speed_table[] = {
#ifdef B50
50, B50,
#endif
#ifdef B75
75, B75,
#endif
#ifdef B110
110, B110,
#endif
#ifdef B134
134, B134,
#endif
#ifdef B150
150, B150,
#endif
#ifdef B200
200, B200,
#endif
#ifdef B300
300, B300,
#endif
#ifdef B600
600, B600,
#endif
#ifdef B900
900, B900,
#endif
#ifdef B1200
1200, B1200,
#endif
#ifdef B1800
1800, B1800,
#endif
#ifdef B2400
2400, B2400,
#endif
#ifdef B3600
3600, B3600,
#endif
#ifdef B4800
4800, B4800,
#endif
#ifdef B7200
7200, B7200,
#endif
#ifdef B9600
9600, B9600,
#endif
#ifdef B19200
19200, B19200,
#endif
#ifdef B38400
38400, B38400,
#endif
#ifdef B57600
57600, B57600,
#endif
#ifdef B115200
115200, B115200,
#endif
#ifdef B230400
230400, B230400,
#endif
#ifdef B460800
460800, B460800,
#endif
-1, 0
};
/*---------------------------------------------------------------------------*/
static int
find_speed(speed)
long speed;
{
int i;
i = 0;
while (speed_table[i].speed < speed && speed_table[i+1].speed > 0)
i++;
return i;
}
/*---------------------------------------------------------------------------*/
/* Initialize asynch port "dev" */
int
asy_init(dev,ifp,arg1,arg2,bufsize,trigchar,monitor,speed,force,triglevel)
int dev;
struct iface *ifp;
char *arg1,*arg2; /* Attach args for address and vector */
int16 bufsize;
int trigchar;
char monitor;
long speed;
int force;
int triglevel;
{
register struct asy *ap;
char filename[80];
char *ifn;
int sp, fd;
struct termios termios;
ap = &Asy[dev];
/* UUCP locking with ASCII pid */
strcpy(ap->uulock, "/usr/spool/uucp/LCK..");
strcat(ap->uulock, arg1);
for (;;)
{
if ((fd = open(ap->uulock, O_WRONLY|O_CREAT|O_EXCL, 0644)) != -1 ||
errno != EEXIST)
break;
/* read pid, unlink and retry if proc no longer exists */
if ((fd = open(ap->uulock, O_RDONLY)) == -1)
continue; /* timing is everything */
filename[read(fd, filename, 10)] = '\0';
close(fd);
sscanf(filename, "%d", &fd);
if (kill(fd, 0) == -1 && errno == ESRCH)
{
tprintf("Removing stale lockfile for %s\n", arg1);
unlink(ap->uulock);
continue;
}
tprintf("/dev/%s is locked\n", arg1);
ap->uulock[0] = '\0'; /* so it won't clobber existing lock */
goto Fail;
}
if (fd == -1)
{
tprintf("Can't lock /dev/%s: %s\n", arg1, strerror(errno));
ap->uulock[0] = '\0';
goto Fail;
}
chmod(ap->uulock, 0644); /* beware of overly restrictive umask */
sprintf(filename, "%10d\n", getpid());
write(fd, filename, 11);
close(fd);
strcpy(filename, "/dev/");
strcat(filename, arg1);
if ((fd = open(filename, O_RDWR|O_NONBLOCK|O_NOCTTY, 0644)) == -1)
{
tprintf("Can't open port: %s\n", strerror(errno));
goto Fail;
}
ap->flags = 0;
ap->iface = ifp;
sp = find_speed(speed);
ap->speed = speed_table[sp].speed;
memset((char *) &termios, 0, sizeof(termios));
termios.c_iflag = IGNBRK|IGNPAR;
termios.c_cflag = CS8|CREAD|CLOCAL|speed_table[sp].flags;
termios.c_cc[VTIME] = 2;
if ((ap->pktsize = triglevel & 255))
termios.c_cc[VMIN] = triglevel & 255;
if (cfsetispeed(&termios, speed_table[sp].flags) == -1)
{
tprintf("Can't set speed: %s\n", strerror(errno));
goto Fail;
}
if (cfsetospeed(&termios, speed_table[sp].flags) == -1)
{
tprintf("Can't set speed: %s\n", strerror(errno));
goto Fail;
}
if (tcsetattr(fd, TCSANOW, &termios) == -1)
{
tprintf("Can't configure port: %s\n", strerror(errno));
goto Fail;
}
/* security: port won't work until re-opened */
if ((ap->fd = open(filename, O_RDWR|O_NONBLOCK|O_NOCTTY, 0644)) == -1)
{
tprintf("Can't reopen port: %s\n", strerror(errno));
goto Fail;
}
close(fd);
/* new asy parameters, defaulted for now */
ap->rxq = 1; /* disable queueing */
ap->txq = 1;
ap->rxbuf = bufsize;
/* clear statistics */
ap->rxints = 0;
ap->txints = 0;
ap->rxchar = 0;
ap->txchar = 0;
ap->rxput = 0;
ap->rxovq = 0;
ap->rxblock = 0;
ap->txget = 0;
ap->txovq = 0;
ap->txblock = 0;
ifp->txproc = newproc(ifn = if_name(ifp," tx"),
256, asy_tx, dev, ifp, NULL, 0);
free(ifn);
ap->rxproc = newproc(ifn = if_name(ifp, " hw"),
256, asy_input, dev, ifp, NULL, 0);
free(ifn);
register_io(ap->fd, &ap->fd);
return 0;
Fail:
rflush(); /* make sure the message gets out */
if (fd != -1)
close(fd);
/* Unlock port */
if (ap->uulock[0])
unlink(ap->uulock);
ap->uulock[0] = '\0';
ap->iface = NULLIF;
return -1;
}
/*---------------------------------------------------------------------------*/
int
asy_stop(ifp)
struct iface *ifp;
{
register struct asy *ap;
ap = &Asy[ifp->dev];
if(ap->iface == NULLIF)
return -1; /* Not allocated */
unregister_io(ap->fd);
if (ifp->txproc)
killproc(ifp->txproc);
ifp->txproc = 0;
if (ap->rxproc)
killproc(ap->rxproc);
ap->rxproc = 0;
ap->iface = NULLIF;
free_q(&ap->sndq);
close(ap->fd);
free_q(&ap->rcvq);
if (ap->uulock[0])
unlink(ap->uulock);
ap->uulock[0] = '\0';
return 0;
}
void
detach_all_asy()
{
register struct asy *ap;
for (ap = Asy; ap != Asy + ASY_MAX; ap++)
{
if(ap->iface == NULLIF)
break;
unregister_io(ap->fd);
if (ap->iface->txproc)
killproc(ap->iface->txproc);
ap->iface->txproc = 0;
if (ap->rxproc)
killproc(ap->rxproc);
ap->rxproc = 0;
ap->iface = NULLIF;
free_q(&ap->sndq);
free_q(&ap->rcvq);
close(ap->fd);
if (ap->uulock[0])
unlink(ap->uulock);
ap->uulock[0] = '\0';
}
}
/*---------------------------------------------------------------------------*/
/* Set asynch line speed */
int
asy_speed(dev, bps)
int dev;
long bps;
{
struct asy *asyp;
int sp;
struct termios termios;
if(bps <= 0 || dev >= ASY_MAX)
return -1;
asyp = &Asy[dev];
if(asyp->iface == NULLIF)
return -1;
if(bps == 0)
return -1;
sp = find_speed(bps);
if (tcgetattr(asyp->fd, &termios))
return -1;
if (cfsetispeed(&termios, speed_table[sp].flags))
return -1;
if (cfsetospeed(&termios, speed_table[sp].flags))
return -1;
termios.c_cflag &= ~CBAUD;
termios.c_cflag |= speed_table[sp].flags;
if (tcsetattr(asyp->fd, TCSANOW, &termios))
return -1;
asyp->speed = speed_table[sp].speed;
return 0;
}
/* Set termios VMIN (packet size) */
int
asy_vmin(dev, pktsize)
int dev;
long pktsize;
{
struct termios termios;
struct asy *asyp;
if (pktsize < 0 || pktsize > 255 || dev >= ASY_MAX)
return -1;
if ((asyp = &Asy[dev])->iface == NULLIF)
return -1;
if (tcgetattr(asyp->fd, &termios))
return -1;
termios.c_cc[VMIN] = asyp->pktsize = pktsize & 255;
if (tcsetattr(asyp->fd, TCSANOW, &termios))
return -1;
return 0;
}
/* Set or clear RTS/CTS flow control */
int
asy_rts(dev, onoff)
int dev;
unsigned long onoff;
{
struct termios termios;
struct asy *asyp;
if ((asyp = &Asy[dev])->iface == NULLIF)
return -1;
if (tcgetattr(asyp->fd, &termios))
return -1;
/*
* note sense is reversed as per DOS version, except that we default to
* RTS/CTS being *off* for compatibility with earlier ALPHA versions
*/
if (onoff)
{
asyp->flags &= ~ASY_RTSCTS;
termios.c_cflag &= ~CRTSCTS;
}
else
{
asyp->flags |= ASY_RTSCTS;
termios.c_cflag |= CRTSCTS;
}
if (tcsetattr(asyp->fd, TCSANOW, &termios))
return -1;
return 0;
}
/*---------------------------------------------------------------------------*/
/* Asynchronous line I/O control */
int32
asy_ioctl(ifp, cmd, set, val)
struct iface *ifp;
int cmd;
int set;
int32 val;
{
struct asy *ap = &Asy[ifp->dev];
switch(cmd){
case PARAM_SPEED:
if (set)
asy_speed(ifp->dev, val);
return ap->speed;
case PARAM_MIN:
if (set)
asy_vmin(ifp->dev, val);
return ap->pktsize;
case PARAM_RTS:
if (set)
asy_rts(ifp->dev, val);
return (ap->flags & ASY_RTSCTS) == 0;
}
return -1;
}
/*---------------------------------------------------------------------------*/
static void
asy_input(dev, arg1, arg2)
int dev;
void *arg1, *arg2;
{
struct timeval tv;
extern int errno;
struct asy *ap;
fd_set fds;
char *buf;
int i, c;
ap = &Asy[dev];
for (;;)
{
if (pwait(&ap->fd) != 0)
return;
ap->rxints++;
buf = mallocw(ap->rxbuf);
if (ap->pktsize)
fcntl(ap->fd, F_SETFL, fcntl(ap->fd, F_GETFL, 0) & ~O_NONBLOCK);
if ((i = read(ap->fd, buf, ap->rxbuf)) == 0 ||
(!ap->pktsize && i == -1 && errno == EWOULDBLOCK))
{
if (ap->pktsize)
fcntl(ap->fd, F_SETFL, fcntl(ap->fd, F_GETFL, 0) | O_NONBLOCK);
ap->rxblock++;
free(buf);
continue;
}
if (i == -1)
{
tprintf("asy_input(%d): read error %d, shutting down\n", dev,
errno);
if (ap->pktsize)
fcntl(ap->fd, F_SETFL, fcntl(ap->fd, F_GETFL, 0) | O_NONBLOCK);
free(buf);
return;
}
if ((c = ap->rxq) <= 0)
c = 1;
while (i > 0 && c > 0)
{
ap->rxchar += i;
ap->rxput++;
enqueue(&ap->rcvq, qdata(buf, i));
c--;
if (ap->pktsize)
{
/* can't just read to check for data, since it might block */
FD_ZERO(&fds);
FD_SET(ap->fd, &fds);
tv.tv_sec = tv.tv_usec = 0;
if (select(FD_SETSIZE, &fds, 0, 0, &tv) == 0)
{
i = -1;
errno = EWOULDBLOCK;
break;
}
}
i = read(ap->fd, buf, ap->rxbuf);
}
free(buf);
if (i == -1 && errno != EWOULDBLOCK)
{
tprintf("asy_input(%d): read error %d, shutting down\n", dev,
errno);
return;
}
if (ap->pktsize)
fcntl(ap->fd, F_SETFL, fcntl(ap->fd, F_GETFL, 0) | O_NONBLOCK);
if (c < 1 && ap->rxq > 1)
ap->rxovq++;
}
}
int
get_asy(dev)
int dev;
{
struct asy *ap;
ap = &Asy[dev];
if (ap->iface == NULLIF)
return -1;
while (!ap->rcvq)
{
if (pwait(&ap->rcvq) != 0)
return -1; /* may not be dead, e.g. alarm in dialer */
}
return PULLCHAR(&ap->rcvq);
}
int
doasystat(int argc,char **argv,void *p)
{
register struct asy *asyp;
struct iface *ifp;
int i;
if(argc < 2){
for(asyp = Asy;asyp < &Asy[ASY_MAX];asyp++){
if(asyp->iface != NULLIF)
pasy(asyp);
}
return 0;
}
for(i=1;i<argc;i++){
if((ifp = if_lookup(argv[i])) == NULLIF){
tprintf("Interface %s unknown\n",argv[i]);
continue;
}
for(asyp = Asy;asyp < &Asy[ASY_MAX];asyp++){
if(asyp->iface == ifp){
pasy(asyp);
break;
}
}
if(asyp == &Asy[ASY_MAX])
tprintf("Interface %s not asy\n",argv[i]);
}
return 0;
}
/*---------------------------------------------------------------------------*/
static void
pasy(asyp)
struct asy *asyp;
{
tprintf("%s: %lu bps, ", asyp->iface->name, asyp->speed);
if (asyp->pktsize)
tprintf("packet size %d", asyp->pktsize);
else
tprintf("non-blocking");
tprintf(", RTS/CTS %sabled", (asyp->flags & ASY_RTSCTS? "en": "dis"));
tprintf(", carrier %sabled\n", (asyp->flags & ASY_CARR? "en": "dis"));
tprintf(" RX: ints %lu chars %lu puts %lu buf %d rxqueue %d qlen %d ovq "
"%ld block %ld\n",
asyp->rxints, asyp->rxchar, asyp->rxput, asyp->rxbuf, asyp->rxq,
len_q(asyp->rcvq), asyp->rxovq, asyp->rxblock);
tprintf(" TX: ints %lu gets %lu chars %lu txqueue %d qlen %d ovq %ld "
"block %ld\n",
asyp->txints, asyp->txget, asyp->txchar, asyp->txq,
len_q(asyp->sndq), asyp->txovq, asyp->txblock);
}
/*---------------------------------------------------------------------------*/
/* Serial transmit process, common to all protocols */
/*
* Yes, this badly needs to be rewritten.
*/
static void
asy_tx(dev, p1, p2)
int dev;
void *p1, *p2;
{
register struct mbuf *bp;
struct asy *asyp;
int c, l, off;
asyp = &Asy[dev];
if ((c = asyp->txq) <= 0)
c = 1;
for (;;)
{
while (asyp->sndq == NULLBUF)
{
if (!(c = asyp->txq))
c = 1;
if (pwait(&asyp->sndq) != 0)
return;
asyp->txints++;
}
bp = dequeue(&asyp->sndq);
asyp->txget++;
off = 0;
while (bp != NULLBUF)
{
l = write(asyp->fd, bp->data + off, (size_t)bp->cnt - off);
if (l == -1 && errno != EWOULDBLOCK)
{
tprintf("asy_tx(%d): write error %d, shutting down\n", dev,
errno);
return;
}
if (l == -1)
l = 0;
asyp->txchar += l;
if (l == bp->cnt - off)
{
bp = free_mbuf(bp);
off = 0;
}
else
{
asyp->txblock++;
pwait(NULL);
off += l;
}
}
if (--c < 1 && asyp->txq > 1)
{
asyp->txovq++;
pwait(NULL);
}
}
}
/*---------------------------------------------------------------------------*/
/* Send a message on the specified serial line */
int
asy_send(dev,bp)
int dev;
struct mbuf *bp;
{
struct asy *asyp;
if(dev < 0 || dev >= ASY_MAX){
free_p(bp);
return -1;
}
asyp = &Asy[dev];
if(asyp->iface == NULLIF)
free_p(bp);
else
enqueue(&asyp->sndq, bp);
return 0;
}
/* stub, CD not enabled at present */
int
carrier_detect(dev)
int dev;
{
return 1; /* assume always on, with CLOCAL it is! */
}
/*---------------------------------------------------------------------------*/
int
doasyconfig(ac, av, p)
int ac;
char **av;
void *p;
{
struct iface *ip;
struct asy *ap;
if (ac < 3)
return 1;
if (!(ip = if_lookup(av[1])))
{
tprintf("Interface %s unknown\n", av[1]);
return 1;
}
for (ap = Asy; ap < &Asy[ASY_MAX]; ap++)
{
if (ap->iface == ip)
break;
}
if (!ap)
{
tprintf("Interface %s not asy\n", av[1]);
return 1;
}
return subcmd(AsyPcmds, ac - 1, av + 1, ap);
}
static int
doasy2stat(ac, av, d)
int ac;
char **av;
void *d;
{
pasy((struct asy *) d);
return 0;
}
static int
dorxqueue(ac, av, d)
int ac;
char **av;
void *d;
{
return setint(&((struct asy *) d)->rxq, "Receive queue size", ac, av);
}
static int
dotxqueue(ac, av, d)
int ac;
char **av;
void *d;
{
return setint(&((struct asy *) d)->txq, "Transmit queue size", ac, av);
}
static int
dobufsize(ac, av, d)
int ac;
char **av;
void *d;
{
return setuns(&((struct asy *) d)->rxbuf, "Receive buffer size", ac, av);
}